const { exec } = require('child_process');
const request = require('../utils/request');
const { Enviroment, ProjectManage } = require('../model/enviromentModel');
const accessTokenService = require('../service/accessTokenService');
const projectService = require('../service/projectService');
const bashService = require('../service/bashService');
const moment = require('moment');
const GitOpenApi = require('../utils/gitOpenApi');
const uuid = require('../utils/uuid');
const config = require('../config');
const simpleGit = require('simple-git');
const _ = require('lodash');
const { fail } = require('assert');
const { stat } = require('fs');

const localMerge = ({project, branch, mergeBranch, scriptContent }, logs) => {
    const SimpleGit = simpleGit({
        baseDir: `${config.localDir}${project.repo}`,
        binary: 'git',
        maxConcurrentProcesses: 6,
    });
    return new Promise((resolve, reject) => {
        SimpleGit
            .checkout(branch)
            .pull()
            .pull('origin', mergeBranch)
            .then(async () => {
                if(scriptContent) {
                    const result = await bashService.execScript(scriptContent.replace(/\n/g, '&'), `${config.localDir}${project.repo}`);
                    if(result.code === 'ACK') {
                        SimpleGit.add('.')
                            .commit( `Merge branch ${mergeBranch} into ${branch}`)
                            .push('origin', branch);
                        logs.push({
                            name: project.name,
                            status: 'SUCCESS',
                            message: `${project.name}从${mergeBranch}到${branch}合并成功`
                        })
                    } else {
                        console.log('脚步执行错误', result);
                        logs.push({
                            id: uuid(),
                            projectId: project.id,
                            projectName: project.name,
                            status: 'FAIL',
                            message: `${project.name}脚步执行失败, 代码未推送到远程`
                        });
                        resolve();
                    }

                    resolve();
                } else {
                    SimpleGit.push('origin', branch).then(() => {
                        logs.push({
                            name: project.name,
                            status: 'SUCCESS',
                            message: `${project.name}从${mergeBranch}到${branch}合并成功`
                        })
                        resolve();
                    })
                }
            })
            .catch((e) => {
                SimpleGit.reset('hard').then(() => {
                    console.error('failed: ', e)
                    logs.push({
                        id: uuid(),
                        projectId: project.id,
                        projectName: project.name,
                        status: 'FAIL',
                        message: `${project.name}从${mergeBranch}到${branch}合并失败:${e.message}`
                    });
                    resolve();
                });
            });
    })
}
const remoteMerge = async ({project, branch, mergeBranch }, logs) => {
    const gitApi = new GitOpenApi();
    try {
        const accessToken = await accessTokenService.getAccessToken();
        const pr = await gitApi.createPullRequest({
            owner: project.owner,
            repo: project.repo,
            branch, 
            mergeBranch,
            accessToken
        });
        await gitApi.reviewPullRequest({
            owner: project.owner,
            repo: project.repo,
            number: pr.number,
            accessToken
        });
        await gitApi.testPullRequest({
            owner: project.owner,
            repo: project.repo,
            number: pr.number,
            accessToken
        });
        await gitApi.mergePullRequest({
            owner: project.owner,
            repo: project.repo,
            number: pr.number,
            accessToken
        });
        logs.push({
            name: project.name,
            status: 'SUCCESS',
            message: `${project.name}从${mergeBranch}到${branch}合并成功`
        })
    } catch(e) {
        if(e.message.includes('不存在差异')) {
            logs.push({
                name: project.name,
                status: 'SUCCESS',
                message: e.message
            })
        } else {
            logs.push({
                id: uuid(),
                projectId: project.id,
                projectName: project.name,
                prNumber: e && e.payload && e.payload.prNumber,
                status: 'FAIL',
                message: `${project.name}从${mergeBranch}到${branch}合并失败:${e.message}`
            });
        }
    };
    return logs;
}

module.exports = {
    async saveEnv(data) {
        if(data.id) {
            //编辑
            return Enviroment.update(data, {where: {id: data.id}});
        } else {
            //新建
            const newEnv = await Enviroment.create(data);
            const projects = await ProjectManage.findAll({where: {id: data['projects'] && data['projects'].map(p => p.id)}});
            newEnv.addProject_manages(projects);
            return newEnv;
        }
    },
    async findAllEnv() {
        return await Enviroment.findAll({
            include: {
                model: ProjectManage,
                as: "project_manages"
            }
        }).then((data) => {
            return data;
        });
    },
    async mergeProject({id, logId, branch, mergeBranch, projects, scriptContent}) {
        const logs = [];
        const currentEnv = await Enviroment.findOne({ where: { id } });
        let p;
        projects.forEach(async (project) => {
            if(project.isRemoteMerge) {
                p = remoteMerge({project, branch, mergeBranch }, logs);
            } else {
                p = localMerge({project, branch, mergeBranch, scriptContent }, logs);
            }
        })
        await p;
        const currentLogs = JSON.parse(currentEnv.lastMergeLog);
        currentLogs.forEach((log, index) => {
            if(log.id === logId && logs.length > 0) {
                currentLogs[index] = logs[0];
            }
        })
        return Enviroment.update({
            lastMergeLog: JSON.stringify(currentLogs)
        }, {where: {id}}).then(() => {
            return logs;
        })
    },
    async merge({id, name, branch, mergeBranch, projects, scriptContent}) {
        const pojectListFromDb = await projectService.findAllProjectByCode(projects);
        console.log('pojectListFromDb', pojectListFromDb);
        const logs = [];
        const promiseList = []; 
        pojectListFromDb.forEach(async (project) => {
            let p;
            if(project.isRemoteMerge) {
                p = remoteMerge({project, branch, mergeBranch }, logs);
            } else {
                p = localMerge({project, branch, mergeBranch, scriptContent }, logs);
            }
            promiseList.push(p);
        })

        await Promise.all(promiseList);

        return Enviroment.update({
            name,
            branch,
            mergeBranch,
            scriptContent,
            lastMergeTime: Date.now(),
            lastMergeLog: JSON.stringify(logs).slice(0, 2048)
        }, {where: {id}}).then(() => {
            return logs;
        })
    },

    async updatePullRequest({projects, prNumber}) {
        const gitApi = new GitOpenApi();
        const pojectListFromDb = await projectService.findAllProjectByCode(projects);
        const accessToken = await accessTokenService.getAccessToken();
        const promiseList = []; 
        pojectListFromDb.forEach(async (project) => {
            let p = await gitApi.updatePullRequest({
                owner: project.owner,
                repo: project.repo,
                number: prNumber,
                accessToken
            });
            promiseList.push(p);
        })
        await Promise.all(promiseList);
    },

    async createTag({branch, projects, tagName, tagMessage}) {
        const pojectListFromDb = await projectService.findAllProjectByCode(projects);
        const accessToken = await accessTokenService.getAccessToken();
        const promiseList = [];
        pojectListFromDb.forEach((project) => {
            const p = request.post(`https://gitee.com/api/v5/repos/charlestang1/${project.repo}/tags`, {
                    access_token: accessToken,
                    tag_name: tagName,
                    tag_message: tagMessage,
                    refs: branch
            })
            promiseList.push(p);
        });
        const result = await Promise.all(promiseList);
        return result;
    },
    async setProtectBranch({projects, branch}) {
        const pojectListFromDb = await projectService.findAllProjectByCode(projects);
        const accessToken = await accessTokenService.getAccessToken();
        const gitApi = new GitOpenApi();
        const promiseList = [];
        pojectListFromDb.forEach(async (project) => {
            let p;
            p = gitApi.setProtectBranch({
                owner: project.owner,
                repo: project.repo,
                accessToken,
                branch
            });
            promiseList.push(p);
        })
        const result = await Promise.all(promiseList);
        return result;
    }
}